package org.infodavid.common.util;

import java.lang.ref.WeakReference;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.joda.time.DateMidnight;
import org.joda.time.DateTime;
import org.joda.time.LocalDateTime;

/**
 * The Class TimeUtil.
 */
public final class TimeUtil {

	/**
	 * The Enum EPeriodType.
	 */
	public enum EPeriodType {

		/** The HOLIDAY. */
		HOLIDAY,
		/** The NORMAL. */
		NORMAL,
		/** The VACATION. */
		VACATION
	}

	/**
	 * The Enum EPeriod.
	 */
	public enum ETimePeriod {

		/** The DAY. */
		DAY,
		/** The MONTH. */
		MONTH,
		/** The WEEK. */
		WEEK,
		/** The YEAR. */
		YEAR
	}

	/** The Constant log. */
	private static final Log LOGGER = LogFactory.getLog(TimeUtil.class);

	/** The singleton. */
	private static WeakReference<TimeUtil> singleton = null;

	/**
	 * returns the singleton.
	 * @return the utility
	 */
	public static synchronized TimeUtil getInstance() {
		if (singleton == null || singleton.get() == null) {
			singleton = new WeakReference<TimeUtil>(new TimeUtil());
		}

		return singleton.get();
	}

	/** The is debug enabled. */
	@SuppressWarnings("unused")
	private final boolean isDebugEnabled = LOGGER.isDebugEnabled();

	/** The is trace enabled. */
	private final boolean isTraceEnabled = LOGGER.isTraceEnabled();

	/**
	 * Instantiates a new time utility.
	 */
	private TimeUtil() {
		super();
	}

	/**
	 * Compare.
	 * @param o1 the o1
	 * @param o2 the o2
	 * @return the result
	 */
	public int compare(final DateTime o1, final DateTime o2) {
		final int result;

		if (o1 == null && o2 == null) {
			result = 0;
		}
		else if (o1 == null) {
			result = 1;
		}
		else if (o2 == null) {
			result = -1;
		}
		else {
			final DateTime dty;

			if (o1.getZone().equals(o2.getZone())) {
				dty = o2;
			}
			else {
				dty = o2.toDateTime(o1.getZone());
			}

			if (o1.getMillis() == dty.getMillis()) {
				result = 0;
			}
			else if (o1.getMillis() < dty.getMillis()) {
				result = -1;
			}
			else {
				result = 1;
			}
		}

		if (isTraceEnabled) {
			LOGGER.trace("DateTime comparison is: " + result + " for dates:\n" + o1 + '\n' + o2);
		}

		return result;
	}

	/**
	 * Compare.
	 * @param o1 the o1
	 * @param o2 the o2
	 * @return the result
	 */
	public int compare(final DateMidnight o1, final DateMidnight o2) {
		final int result;

		if (o1 == null && o2 == null) {
			result = 0;
		}
		else if (o1 == null) {
			result = 1;
		}
		else if (o2 == null) {
			result = -1;
		}
		else {
			final DateMidnight dty;

			if (o1.getZone().equals(o2.getZone())) {
				dty = o2;
			}
			else {
				dty = o2.toDateTime(o1.getZone()).toDateMidnight();
			}

			if (o1.getMillis() == dty.getMillis()) {
				result = 0;
			}
			else if (o1.getMillis() < dty.getMillis()) {
				result = -1;
			}
			else {
				result = 1;
			}
		}

		if (isTraceEnabled) {
			LOGGER.trace("DateTime comparison is: " + result + " for dates:\n" + o1 + '\n' + o2);
		}

		return result;
	}

	/**
	 * Compare.
	 * @param o1 the o1
	 * @param o2 the o2
	 * @return the result
	 */
	public int compare(final LocalDateTime o1, final LocalDateTime o2) {
		final int result;

		if (o1 == null && o2 == null) {
			result = 0;
		}
		else if (o1 == null) {
			result = 1;
		}
		else if (o2 == null) {
			result = -1;
		}
		else {
			result = o1.compareTo(o2);
		}

		if (isTraceEnabled) {
			LOGGER.trace("LocalDateTime comparison is: " + result + " for dates:\n" + o1 + '\n' + o2);
		}

		return result;
	}

	/**
	 * Equals.
	 * @param o1 the o1
	 * @param o2 the o2
	 * @return true, if successful
	 */
	public boolean equals(final DateTime o1, final DateTime o2) {
		boolean result;

		if (o1 == o2) {
			result = true;
		}
		else if (o1 == null || o2 == null) {
			result = false;
		}
		else {
			final DateTime dty;

			if (o1.getZone().equals(o2.getZone())) {
				dty = o2;
			}
			else {
				dty = o2.toDateTime(o1.getZone());
			}

			result = o1.getMillis() == dty.getMillis();
		}

		if (isTraceEnabled) {
			LOGGER.trace("DateTime comparison is: " + result + " for dates:\n" + o1 + '\n' + o2);
		}

		return result;
	}

	/**
	 * Equals.
	 * @param o1 the o1
	 * @param o2 the o2
	 * @return true, if successful
	 */
	public boolean equals(final DateMidnight o1, final DateMidnight o2) {
		boolean result;

		if (o1 == o2) {
			result = true;
		}
		else if (o1 == null || o2 == null) {
			result = false;
		}
		else {
			final DateMidnight dty;

			if (o1.getZone().equals(o2.getZone())) {
				dty = o2;
			}
			else {
				dty = o2.toDateTime(o1.getZone()).toDateMidnight();
			}

			result = o1.getMillis() == dty.getMillis();
		}

		if (isTraceEnabled) {
			LOGGER.trace("DateTime comparison is: " + result + " for dates:\n" + o1 + '\n' + o2);
		}

		return result;
	}

	/**
	 * Equals.
	 * @param o1 the o1
	 * @param o2 the o2
	 * @return true, if successful
	 */
	public boolean equals(final LocalDateTime o1, final LocalDateTime o2) {
		boolean result;

		if (o1 == o2) {
			result = true;
		}
		else if (o1 == null || o2 == null) {
			result = false;
		}
		else {
			result = o1.equals(o2);
		}

		if (isTraceEnabled) {
			LOGGER.trace("LocalDateTime comparison is: " + result + " for dates:\n" + o1 + '\n' + o2);
		}

		return result;
	}

	/**
	 * Milliseconds to minutes.
	 * @param value the value
	 * @return the value in min
	 */
	public double millisToMinutes(final long value) {
		return value / 60000.0;
	}

	/**
	 * Minutes to milliseconds.
	 * @param value the value
	 * @return the value in ms
	 */
	public long minutesToMillis(final long value) {
		return value * 1000 * 60;
	}
}
